package edu.polsl.bio.str.manager.dao;

import edu.polsl.bio.str.common.constant.NodeState;
import edu.polsl.bio.str.common.entity.Node;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.HibernateCallback;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;

/**
 * //todo class description
 * <p/>
 * Created on: Jun 14, 2011 10:41:20 AM <br/>
 * <a href="http://www.polsl.pl">www.polsl.pl</a>
 *
 * @author lukasz.olczak@polsl.pl
 */
public class NodeDaoImpl extends BaseDao implements NodeDao {
  /**
   * slf4j Logger.
   */
  private static final Logger log = LoggerFactory.getLogger(NodeDaoImpl.class.getName());

  private static final String TIMEOUT_EXCEEDED_SELECT = "from Node n where TIMESTAMPDIFF(MINUTE, n.lastHeartbeat, now()) > :timeout " +
                                                        "and n.nodeState = :state";

  @Override
  public Node findByIp(String nodeIp) {
    DetachedCriteria query = DetachedCriteria.forClass(Node.class).add(Restrictions.eq("nodeIp", nodeIp));
    return getUniqueResult(getHibernateTemplate().findByCriteria(query));
  }

  @Override
  public Node getById(Long nodeId) {
    return (Node) getHibernateTemplate().get(Node.class, nodeId);
  }

  @Override
  public Node getByIdForUpdate(Long nodeId) {
    DetachedCriteria query = DetachedCriteria.forClass(Node.class).add(Restrictions.eq("id", nodeId))
        .setLockMode(LockMode.PESSIMISTIC_WRITE);

    return getUniqueResult(getHibernateTemplate().findByCriteria(query));
  }

  @Override
  public Node create(Node node) {
    Serializable nodeId = getHibernateTemplate().save(node);
    return (Node) getHibernateTemplate().get(Node.class, nodeId);
  }

  @Override
  public List<Node> getIdleNodes() {
    DetachedCriteria query = DetachedCriteria.forClass(Node.class)
        .add(Restrictions.and(Restrictions.gt("tokens", 0), Restrictions.eq("nodeState", NodeState.ATTACHED)))
        .addOrder(Order.desc("tokens")).setLockMode(LockMode.PESSIMISTIC_WRITE);
    return getHibernateTemplate().findByCriteria(query);
  }

  @Override
  public Node update(Node node) {
    getHibernateTemplate().update(node);
    return getById(node.getId());
  }

  @Override
  public List<Node> getTimeoutExceededNodes(final int timeoutInMin) {
    return (List) getHibernateTemplate().execute(new HibernateCallback() {
      @Override
      public Object doInHibernate(Session session) throws HibernateException, SQLException {
        Query query = session.createQuery(TIMEOUT_EXCEEDED_SELECT);
        query.setParameter("state", NodeState.ATTACHED);
        query.setParameter("timeout", timeoutInMin);
        query.setLockMode("n", LockMode.PESSIMISTIC_WRITE);
        return query.list();
      }
    });
  }
}
